// XMLSettings.cpp: implementation of the CXMLSettings class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "XMLSettings.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

CString CXMLNode::m_strTab = _T("\t");

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CXMLSettings::CXMLSettings(BOOL bReadOnly /* = TRUE */, LPCTSTR lpszRoot /* = _T("") */)
{
	m_bReadOnly = bReadOnly;

	if (lpszRoot == _T(""))
		m_Local.m_strName = _T("CURRENT_USER");
	else
		m_Local.m_strName = lpszRoot;

	m_pCurrNode = &m_Local;
	m_posChild = NULL;
}

CXMLSettings::~CXMLSettings()
{
}
//***************************************************************************************
BOOL CXMLSettings::VerifyKey (LPCTSTR /*pszPath*/)
{
	ASSERT (FALSE);	// TODO
	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::VerifyValue (LPCTSTR /*pszValue*/)
{
	ASSERT (FALSE);	// TODO
	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::CreateKey (LPCTSTR pszPath)
{
	if (m_bReadOnly)
	{
		// ASSERT (FALSE);
		return FALSE;
	}

	ASSERT (pszPath != NULL);

	CString strPath = pszPath;
	int iPathLen = strPath.GetLength ();
	if (iPathLen > 0 && strPath [iPathLen - 1] != _T('\\'))
	{
		strPath += _T('\\');
	}

	// CXMLNode* pNode = GetTree ();
	CXMLNode* pNode = m_pCurrNode;

	for (int iFrom = 0; iFrom < strPath.GetLength ();)
	{
		int iEnd = strPath.Find (_T('\\'), iFrom);
		if (iEnd < 0)
		{
			ASSERT (FALSE);
			return FALSE;
		}

		CString strSubKey = strPath.Mid (iFrom, iEnd - iFrom);
		strSubKey.Remove (_T(' '));
		
		CXMLNode* pChild = pNode->FindChild (strSubKey);
		if (pChild == NULL)
		{
			pChild = new CXMLNode;
			pChild->m_strName = strSubKey;
			pNode->AddChild (pChild);
		}

		pNode = pChild;
		iFrom = iEnd + 1;
	}

	m_pCurrNode = pNode;
	m_posChild = m_pCurrNode->m_lstChildren.GetHeadPosition();
	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::CreateKey(LPCTSTR lpszFormat, UINT uiID)
{
	CString str;
	str.Format(lpszFormat, uiID);
	return CreateKey(str);
}
//***************************************************************************************
BOOL CXMLSettings::Open (LPCTSTR pszPath)
{
	ASSERT (pszPath != NULL);

	// CXMLNode* pNode = GetTree ();
	CXMLNode* pNode = m_pCurrNode;

	m_sPath = pszPath;

	CString strPath = pszPath;
	int iPathLen = strPath.GetLength ();
	if (iPathLen > 0 && strPath [iPathLen - 1] != _T('\\'))
	{
		strPath += _T('\\');
	}

	for (int iFrom = 0; iFrom < strPath.GetLength ();)
	{
		int iEnd = strPath.Find (_T('\\'), iFrom);
		if (iEnd < 0)
		{
			ASSERT (FALSE);
			return FALSE;
		}

		CString strSubKey = strPath.Mid (iFrom, iEnd - iFrom);
		strSubKey.Remove (_T(' '));
		
		CXMLNode* pChild = pNode->FindChild (strSubKey);
		if (pChild == NULL)
		{
			// m_pCurrNode = NULL;
			return FALSE;
		}

		pNode = pChild;
		iFrom = iEnd + 1;
	}

	m_pCurrNode = pNode;
	m_posChild = m_pCurrNode->m_lstChildren.GetHeadPosition();
	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::OpenNoCase (LPCTSTR pszPath)
{
	ASSERT (pszPath != NULL);

	// CXMLNode* pNode = GetTree ();
	CXMLNode* pNode = m_pCurrNode;

	m_sPath = pszPath;

	CString strPath = pszPath;
	int iPathLen = strPath.GetLength ();
	if (iPathLen > 0 && strPath [iPathLen - 1] != _T('\\'))
	{
		strPath += _T('\\');
	}

	for (int iFrom = 0; iFrom < strPath.GetLength ();)
	{
		int iEnd = strPath.Find (_T('\\'), iFrom);
		if (iEnd < 0)
		{
			ASSERT (FALSE);
			return FALSE;
		}

		CString strSubKey = strPath.Mid (iFrom, iEnd - iFrom);
		strSubKey.Remove (_T(' '));
		
		CXMLNode* pChild = pNode->FindChildNoCase (strSubKey);
		if (pChild == NULL)
		{
			// m_pCurrNode = NULL;
			return FALSE;
		}

		pNode = pChild;
		iFrom = iEnd + 1;
	}

	m_pCurrNode = pNode;
	m_posChild = m_pCurrNode->m_lstChildren.GetHeadPosition();
	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::Open(LPCTSTR lpszFormat, UINT uiID)
{
	CString str;
	str.Format(lpszFormat, uiID);
	return Open(str);
}
//***************************************************************************************
BOOL CXMLSettings::Back(int nLevel /* = 1 */)
{
	if (nLevel < 0)
	{
		m_pCurrNode = &m_Local;
		m_posChild = m_pCurrNode->m_lstChildren.GetHeadPosition();
		return TRUE;
	}

	while (nLevel && m_pCurrNode->m_pParent)
	{
		m_pCurrNode = m_pCurrNode->m_pParent;
		nLevel--;
	}

	m_posChild = m_pCurrNode->m_lstChildren.GetHeadPosition();
	return (nLevel == 0);
}
//***************************************************************************************
BOOL CXMLSettings::Go(int first, ...)
{
	if (m_pCurrNode == NULL)
		return FALSE;

	int count = 0, i = first;
	va_list marker;
	
	CXMLNode* pNextNode = NULL;
	va_start( marker, first );     /* Initialize variable arguments. */
	while( i != -1 )
	{
		pNextNode = m_pCurrNode->GetChild(i);
		if (pNextNode == NULL)
			break ;
		
		m_pCurrNode = pNextNode;
		count++;
		i = va_arg( marker, int);
	}
	va_end( marker );              /* Reset variable arguments.      */

	m_posChild = m_pCurrNode->m_lstChildren.GetHeadPosition();
	return TRUE;
}
//***************************************************************************************
void CXMLSettings::Close()
{
}
//***************************************************************************************
BOOL CXMLSettings::Write (LPCTSTR pszKey, int iVal)
{
	if (m_bReadOnly)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	CString str;
	str.Format (_T("%d"), iVal);

	return WriteTag (pszKey, str);
}
//***************************************************************************************
BOOL CXMLSettings::Write (LPCTSTR pszKey, DWORD dwVal)
{
	if (m_bReadOnly)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	CString str;
	str.Format (_T("%ld"), dwVal);

	return WriteTag (pszKey, str);
}
//***************************************************************************************
BOOL CXMLSettings::Write (LPCTSTR pszKey, LPCTSTR pszData)
{
	if (m_bReadOnly)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	return WriteTag (pszKey, pszData);
}
//***************************************************************************************
BOOL CXMLSettings::Write (LPCTSTR pszKey, CObject& obj)
{
	if (m_bReadOnly)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	BOOL bRes = FALSE;
	try
	{
		CMemFile file;

		{
			CArchive ar (&file, CArchive::store);

			obj.Serialize (ar);
			ar.Flush ();
		}

#if _MSC_VER >= 1300
		ULONGLONG dwDataSize = file.GetLength ();
#else
		DWORD dwDataSize = file.GetLength ();
#endif
		LPBYTE lpbData = file.Detach ();

		if (lpbData == NULL)
		{
			return FALSE;
		}

		bRes = Write (pszKey, lpbData, (UINT) dwDataSize);
		free (lpbData);
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Write ()!\n"));
		return FALSE;
	}

	return bRes;
}
//***************************************************************************************
BOOL CXMLSettings::Write (LPCTSTR pszKey, CObject* pObj)
{
	if (m_bReadOnly)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	BOOL bRes = FALSE;
	try
	{
		CMemFile file;

		{
			CArchive ar (&file, CArchive::store);
			ar << pObj;
			ar.Flush ();
		}

#if _MSC_VER >= 1300
		ULONGLONG dwDataSize = file.GetLength ();
#else
		DWORD dwDataSize = file.GetLength ();
#endif
		LPBYTE lpbData = file.Detach ();

		if (lpbData == NULL)
		{
			return FALSE;
		}

		bRes = Write (pszKey, lpbData, (UINT) dwDataSize);
		free (lpbData);
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Write ()!\n"));
		return FALSE;
	}

	return bRes;
}
//***************************************************************************************
BOOL CXMLSettings::Write (LPCTSTR pszKey, CWordArray& wcArray)
{
	if (m_bReadOnly)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	BOOL bRes = FALSE;
	try
	{
		CMemFile file;

		{
			CArchive ar (&file, CArchive::store);

			ar << wcArray.GetSize ();
			for (int i = 0; i < wcArray.GetSize (); i ++)
			{
				ar << wcArray [i];
			}

			ar.Flush ();
		}

#if _MSC_VER >= 1300
		ULONGLONG dwDataSize = file.GetLength ();
#else
		DWORD dwDataSize = file.GetLength ();
#endif
		LPBYTE lpbData = file.Detach ();

		if (lpbData == NULL)
		{
			return FALSE;
		}

		bRes = Write (pszKey, lpbData, (UINT) dwDataSize);
		free (lpbData);
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Write ()!\n"));
		return FALSE;
	}

	return bRes;
}
//***************************************************************************************
BOOL CXMLSettings::Write(LPCTSTR pszKey, const CRect& rect)
{
	if (m_bReadOnly)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	BOOL bRes = FALSE;
	try
	{
		CMemFile file;

		{
			CArchive ar (&file, CArchive::store);

			ar << rect;
			ar.Flush ();
		}

#if _MSC_VER >= 1300
		ULONGLONG dwDataSize = file.GetLength ();
#else
		DWORD dwDataSize = file.GetLength ();
#endif
		LPBYTE lpbData = file.Detach ();

		if (lpbData == NULL)
		{
			return FALSE;
		}

		bRes = Write (pszKey, lpbData, (UINT) dwDataSize);
		free (lpbData);
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Write ()!\n"));
		return FALSE;
	}

	return bRes;
}
//***************************************************************************************
BOOL CXMLSettings::Write(LPCTSTR pszKey, const CPoint& point)
{
	if (m_bReadOnly)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	BOOL bRes = FALSE;
	try
	{
		CMemFile file;

		{
			CArchive ar (&file, CArchive::store);

			ar << point;
			ar.Flush ();
		}

#if _MSC_VER >= 1300
		ULONGLONG dwDataSize = file.GetLength ();
#else
		DWORD dwDataSize = file.GetLength ();
#endif
		LPBYTE lpbData = file.Detach ();

		if (lpbData == NULL)
		{
			return FALSE;
		}

		bRes = Write (pszKey, lpbData, (UINT) dwDataSize);
		free (lpbData);
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Write ()!\n"));
		return FALSE;
	}

	return bRes;
}
//***************************************************************************************
BOOL CXMLSettings::Write(LPCTSTR pszKey, const CSize& size)
{
	if (m_bReadOnly)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	BOOL bRes = FALSE;
	try
	{
		CMemFile file;

		{
			CArchive ar (&file, CArchive::store);

			ar << size;
			ar.Flush ();
		}

#if _MSC_VER >= 1300
		ULONGLONG dwDataSize = file.GetLength ();
#else
		DWORD dwDataSize = file.GetLength ();
#endif
		LPBYTE lpbData = file.Detach ();

		if (lpbData == NULL)
		{
			return FALSE;
		}

		bRes = Write (pszKey, lpbData, (UINT) dwDataSize);
		free (lpbData);
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Write ()!\n"));
		return FALSE;
	}

	return bRes;
}
//***************************************************************************************
BOOL CXMLSettings::Write(LPCTSTR pszKey, short& sValue)
{
	if (m_bReadOnly)
	{
		ASSERT (FALSE);
		return FALSE;
	}
	
	CString str;
	str.Format (_T("%d"), sValue);
	
	return WriteTag (pszKey, str);
}
//***************************************************************************************
BOOL CXMLSettings::Write(LPCTSTR pszKey, long& lValue)
{
	if (m_bReadOnly)
	{
		ASSERT (FALSE);
		return FALSE;
	}
	
	CString str;
	str.Format (_T("%ld"), lValue);
	
	return WriteTag (pszKey, str);
}
//***************************************************************************************
BOOL CXMLSettings::Write(LPCTSTR pszKey, float& fValue)
{
	if (m_bReadOnly)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	BOOL bRes = FALSE;
	try
	{
		CMemFile file;

		{
			CArchive ar (&file, CArchive::store);

			ar << fValue;
			ar.Flush ();
		}

#if _MSC_VER >= 1300
		ULONGLONG dwDataSize = file.GetLength ();
#else
		DWORD dwDataSize = file.GetLength ();
#endif
		LPBYTE lpbData = file.Detach ();

		if (lpbData == NULL)
		{
			return FALSE;
		}

		bRes = Write (pszKey, lpbData, (UINT) dwDataSize);
		free (lpbData);
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Write ()!\n"));
		return FALSE;
	}

	return bRes;
}

//***************************************************************************************
BOOL CXMLSettings::Write(LPCTSTR pszKey, double& dValue)
{
	if (m_bReadOnly)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	BOOL bRes = FALSE;
	try
	{
		CMemFile file;

		{
			CArchive ar (&file, CArchive::store);

			ar << dValue;
			ar.Flush ();
		}

#if _MSC_VER >= 1300
		ULONGLONG dwDataSize = file.GetLength ();
#else
		DWORD dwDataSize = file.GetLength ();
#endif
		LPBYTE lpbData = file.Detach ();

		if (lpbData == NULL)
		{
			return FALSE;
		}

		bRes = Write (pszKey, lpbData, (UINT) dwDataSize);
		free (lpbData);
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Write ()!\n"));
		return FALSE;
	}

	return bRes;
}
//***************************************************************************************
BOOL CXMLSettings::Write(LPCTSTR pszKey, UINT& uiValue)
{
	if (m_bReadOnly)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	BOOL bRes = FALSE;
	try
	{
		CMemFile file;

		{
			CArchive ar (&file, CArchive::store);

			ar << uiValue;
			ar.Flush ();
		}

#if _MSC_VER >= 1300
		ULONGLONG dwDataSize = file.GetLength ();
#else
		DWORD dwDataSize = file.GetLength ();
#endif
		LPBYTE lpbData = file.Detach ();

		if (lpbData == NULL)
		{
			return FALSE;
		}

		bRes = Write (pszKey, lpbData, (UINT) dwDataSize);
		free (lpbData);
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Write ()!\n"));
		return FALSE;
	}

	return bRes;
}
//***************************************************************************************
BOOL CXMLSettings::Write(LPCTSTR pszKey, USHORT& usValue)
{
	if (m_bReadOnly)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	BOOL bRes = FALSE;
	try
	{
		CMemFile file;

		{
			CArchive ar (&file, CArchive::store);

			ar << usValue;
			ar.Flush ();
		}

#if _MSC_VER >= 1300
		ULONGLONG dwDataSize = file.GetLength ();
#else
		DWORD dwDataSize = file.GetLength ();
#endif
		LPBYTE lpbData = file.Detach ();

		if (lpbData == NULL)
		{
			return FALSE;
		}

		bRes = Write (pszKey, lpbData, (UINT) dwDataSize);
		free (lpbData);
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Write ()!\n"));
		return FALSE;
	}

	return bRes;
}
//***************************************************************************************
BOOL CXMLSettings::Write (LPCTSTR pszKey, LPBYTE pData, UINT nBytes)
{
	if (m_bReadOnly)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	ASSERT (pszKey != NULL);
	ASSERT (pData != NULL);
	ASSERT (AfxIsValidAddress (pData, nBytes, FALSE));

	LPTSTR lpszBuffer = new TCHAR [nBytes * 2 + 1];
	ASSERT (lpszBuffer != NULL);

	lpszBuffer [0] = 0;

	char lpszByte [3];
	int j = 0;

	for (UINT i = 0; i < nBytes; i++)
	{
		sprintf (lpszByte, "%02x", pData [i]);

		lpszBuffer [j++] = lpszByte [0];
		lpszBuffer [j++] = lpszByte [1];
	}

	lpszBuffer [j] = 0;

	BOOL bRes = WriteTag (pszKey, lpszBuffer);

	delete [] lpszBuffer;
	return bRes;
}
//***************************************************************************************
BOOL CXMLSettings::Write (LPCTSTR pszKey, VARIANT& data)
{
	if (m_bReadOnly)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	ASSERT (pszKey != NULL);
	ASSERT (data.vt = VT_ARRAY | VT_UI1);
	ASSERT (data.parray != NULL);
	// ASSERT (AfxIsValidAddress (pData, nBytes, FALSE));
	
	LPBYTE pData = (LPBYTE)data.parray->pvData;
	UINT nBytes = data.parray->cbElements;

	LPTSTR lpszBuffer = new TCHAR [nBytes * 2 + 1];
	ASSERT (lpszBuffer != NULL);

	lpszBuffer [0] = 0;

	char lpszByte [3];
	int j = 0;

	for (UINT i = 0; i < nBytes; i++)
	{
		sprintf (lpszByte, "%02x", pData [i]);

		lpszBuffer [j++] = lpszByte [0];
		lpszBuffer [j++] = lpszByte [1];
	}

	lpszBuffer [j] = 0;

	BOOL bRes = WriteTag (pszKey, lpszBuffer);

	delete [] lpszBuffer;
	return bRes;
}
//***************************************************************************************
BOOL CXMLSettings::WriteTag (LPCTSTR pszKey, LPCTSTR lpszBuffer)
{
	ASSERT (pszKey != NULL);
	ASSERT (lpszBuffer != NULL);

	if (m_bReadOnly)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	if (m_pCurrNode == NULL)
	{
		ASSERT (FALSE);
		return FALSE;
	}

	ASSERT_VALID (m_pCurrNode);

	CString strKey = pszKey;
	strKey.Remove (_T(' '));

	CXMLNode* pNode = m_pCurrNode->FindChild (strKey);
	if (pNode == NULL)
	{
		pNode = new CXMLNode();
		pNode->m_strName = strKey;
		m_pCurrNode->AddChild (pNode);
	}

	pNode->m_strValue = lpszBuffer;
	return (DWORD)pNode;
}
//***************************************************************************************
BOOL CXMLSettings::Read(LPCTSTR pszKey, int& iVal)
{
	LPCTSTR lpszVal = ReadTag (pszKey);
	if (lpszVal == NULL)
	{
		return FALSE;
	}

	iVal = _ttoi (lpszVal);
	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::Read (LPCTSTR pszKey, DWORD& dwVal)
{
	LPCTSTR lpszVal = ReadTag (pszKey);
	if (lpszVal == NULL)
	{
		return FALSE;
	}

	dwVal = _ttol (lpszVal);
	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::Read (LPCTSTR pszKey, CString& sVal)
{
	LPCTSTR lpszVal = ReadTag (pszKey);
	if (lpszVal == NULL)
	{
		sVal.Empty ();
		return FALSE;
	}

	sVal = lpszVal;
	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::Read (LPCTSTR pszKey, CWordArray& wcArray)
{
	wcArray.SetSize (0);

	BOOL	bSucess = FALSE;
	BYTE*	pData = NULL;
	UINT	uDataSize;

	if (!Read (pszKey, &pData, &uDataSize))
	{
		ASSERT (pData == NULL);
		return FALSE;
	}

	ASSERT (pData != NULL);

	try
	{
		CMemFile file (pData, uDataSize);
		CArchive ar (&file, CArchive::load);

		int iSize;
		ar >> iSize;

		wcArray.SetSize (iSize);
		for (int i = 0; i < iSize; i ++)
		{
			ar >> wcArray [i];
		}

		bSucess = TRUE;
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Read ()!\n"));
	}
	catch (CArchiveException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("CArchiveException exception in CXMLSettings::Read ()!\n"));
	}

	delete pData;
	return bSucess;
}
//***************************************************************************************
BOOL CXMLSettings::Read(LPCTSTR pszKey, CRect& rect)
{
	BOOL	bSucess = FALSE;
	BYTE*	pData = NULL;
	UINT	uDataSize;

	if (!Read (pszKey, &pData, &uDataSize))
	{
		ASSERT (pData == NULL);
		return FALSE;
	}

	ASSERT (pData != NULL);

	try
	{
		CMemFile file (pData, uDataSize);
		CArchive ar (&file, CArchive::load);

		ar >> rect;
		bSucess = TRUE;
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Read ()!\n"));
	}
	catch (CArchiveException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("CArchiveException exception in CXMLSettings::Read ()!\n"));
	}

	delete pData;
	return bSucess;
}
//***************************************************************************************
BOOL CXMLSettings::Read(LPCTSTR pszKey, CSize& size)
{
	BOOL	bSucess = FALSE;
	BYTE*	pData = NULL;
	UINT	uDataSize;

	if (!Read (pszKey, &pData, &uDataSize))
	{
		ASSERT (pData == NULL);
		return FALSE;
	}

	ASSERT (pData != NULL);

	try
	{
		CMemFile file (pData, uDataSize);
		CArchive ar (&file, CArchive::load);

		ar >> size;
		bSucess = TRUE;
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Read ()!\n"));
	}
	catch (CArchiveException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("CArchiveException exception in CXMLSettings::Read ()!\n"));
	}

	delete pData;
	return bSucess;
}
//***************************************************************************************
BOOL CXMLSettings::Read(LPCTSTR pszKey, short& sValue)
{
	LPCTSTR lpszVal = ReadTag (pszKey);
	if (lpszVal == NULL)
	{
		return FALSE;
	}
	
	sValue = (short) _ttoi (lpszVal);
	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::Read(LPCTSTR pszKey, long& lValue)
{
	LPCTSTR lpszVal = ReadTag (pszKey);
	if (lpszVal == NULL)
	{
		return FALSE;
	}
	
	lValue = _ttol (lpszVal);
	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::Read(LPCTSTR pszKey, float& fValue)
{
	BOOL	bSucess = FALSE;
	BYTE*	pData = NULL;
	UINT	uDataSize;

	if (!Read (pszKey, &pData, &uDataSize))
	{
		ASSERT (pData == NULL);
		return FALSE;
	}

	ASSERT (pData != NULL);

	try
	{
		CMemFile file (pData, uDataSize);
		CArchive ar (&file, CArchive::load);

		ar >> fValue;
		bSucess = TRUE;
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Read ()!\n"));
	}
	catch (CArchiveException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("CArchiveException exception in CXMLSettings::Read ()!\n"));
	}

	delete pData;
	return bSucess;
}
//***************************************************************************************
BOOL CXMLSettings::Read(LPCTSTR pszKey, double& dValue)
{
	BOOL	bSucess = FALSE;
	BYTE*	pData = NULL;
	UINT	uDataSize;

	if (!Read (pszKey, &pData, &uDataSize))
	{
		ASSERT (pData == NULL);
		return FALSE;
	}

	ASSERT (pData != NULL);

	try
	{
		CMemFile file (pData, uDataSize);
		CArchive ar (&file, CArchive::load);

		ar >> dValue;
		bSucess = TRUE;
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Read ()!\n"));
	}
	catch (CArchiveException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("CArchiveException exception in CXMLSettings::Read ()!\n"));
	}

	delete pData;
	return bSucess;
}
//***************************************************************************************
BOOL CXMLSettings::Read(LPCTSTR pszKey, UINT& uiValue)
{
	BOOL	bSucess = FALSE;
	BYTE*	pData = NULL;
	UINT	uDataSize;

	if (!Read (pszKey, &pData, &uDataSize))
	{
		ASSERT (pData == NULL);
		return FALSE;
	}

	ASSERT (pData != NULL);

	try
	{
		CMemFile file (pData, uDataSize);
		CArchive ar (&file, CArchive::load);

		ar >> uiValue;
		bSucess = TRUE;
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Read ()!\n"));
	}
	catch (CArchiveException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("CArchiveException exception in CXMLSettings::Read ()!\n"));
	}

	delete pData;
	return bSucess;
}
//***************************************************************************************
BOOL CXMLSettings::Read(LPCTSTR pszKey, USHORT& usValue)
{
	BOOL	bSucess = FALSE;
	BYTE*	pData = NULL;
	UINT	uDataSize;

	if (!Read (pszKey, &pData, &uDataSize))
	{
		ASSERT (pData == NULL);
		return FALSE;
	}

	ASSERT (pData != NULL);

	try
	{
		CMemFile file (pData, uDataSize);
		CArchive ar (&file, CArchive::load);

		ar >> usValue;
		bSucess = TRUE;
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Read ()!\n"));
	}
	catch (CArchiveException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("CArchiveException exception in CXMLSettings::Read ()!\n"));
	}

	delete pData;
	return bSucess;
}
//***************************************************************************************
BOOL CXMLSettings::Read(LPCTSTR pszKey, CPoint& point)
{
	BOOL	bSucess = FALSE;
	BYTE*	pData = NULL;
	UINT	uDataSize;

	if (!Read (pszKey, &pData, &uDataSize))
	{
		ASSERT (pData == NULL);
		return FALSE;
	}

	ASSERT (pData != NULL);

	try
	{
		CMemFile file (pData, uDataSize);
		CArchive ar (&file, CArchive::load);

		ar >> point;
		bSucess = TRUE;
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Read ()!\n"));
	}
	catch (CArchiveException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("CArchiveException exception in CXMLSettings::Read ()!\n"));
	}

	delete pData;
	return bSucess;
}
//***************************************************************************************
BOOL CXMLSettings::Read (LPCTSTR pszKey, LPBYTE pData, UINT& nBytes)
{
	LPCTSTR lpszVal = ReadTag (pszKey);
	if (lpszVal == NULL)
	{
		return FALSE;
	}
	
	int nLen = lstrlen (lpszVal);
	if ((nLen % 2) != 0)
	{
		ASSERT (FALSE);
		return FALSE;
	}
	
	nBytes = nLen / 2;
	
	if (!AfxIsValidAddress(pData, nBytes))
	{
		ASSERT (FALSE);
		return FALSE;
	}
	
	TCHAR szByte [3];
	szByte [2] = 0;
	
	int j = 0;
	for (int i = 0; i < nLen; i += 2)
	{
		szByte [0] = lpszVal [i];
		szByte [1] = lpszVal [i + 1];
		
		int nValue = 0;
		_stscanf (szByte, _T("%x"), &nValue);
		
		BYTE b = (BYTE) nValue;
		(pData) [j++] = b;
	}
	
	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::Read (LPCTSTR pszKey, BYTE** ppData, UINT* pBytes)
{
	LPCTSTR lpszVal = ReadTag (pszKey);
	if (lpszVal == NULL)
	{
		return FALSE;
	}

	int nLen = lstrlen (lpszVal);
	if ((nLen % 2) != 0)
	{
		// ASSERT (FALSE);
		return FALSE;
	}

	*pBytes = nLen / 2;
	*ppData = new BYTE [*pBytes];

	TCHAR szByte [3];
	szByte [2] = 0;

	int j = 0;
	for (int i = 0; i < nLen; i += 2)
	{
		szByte [0] = lpszVal [i];
		szByte [1] = lpszVal [i + 1];

		int nValue = 0;
		_stscanf (szByte, _T("%x"), &nValue);

		BYTE b = (BYTE) nValue;
		(*ppData) [j++] = b;
	}
	
	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::Read (LPCTSTR pszKey, VARIANT& data)
{
	LPCTSTR lpszVal = ReadTag (pszKey);
	if (lpszVal == NULL)
	{
		return FALSE;
	}

	int nLen = lstrlen (lpszVal);
	if ((nLen % 2) != 0)
	{
		// ASSERT (FALSE);
		return FALSE;
	}

	SAFEARRAY  *psa;
	SAFEARRAYBOUND rgsabound[1];
    rgsabound[0].lLbound = 0;
	rgsabound[0].cElements = nLen / 2;
	psa = SafeArrayCreate(VT_UI1, 1, rgsabound);

	TCHAR szByte [3];
	szByte [2] = 0;

	long j = 0;
	for (int i = 0; i < nLen; i += 2)
	{
		szByte [0] = lpszVal [i];
		szByte [1] = lpszVal [i + 1];

		int nValue = 0;
		_stscanf (szByte, _T("%x"), &nValue);

		BYTE b = (BYTE) nValue;
		SafeArrayPutElement (psa, &j, &b);
		j++;
	}
	psa->cbElements = j;
	data.vt = VT_ARRAY | VT_UI1;
	data.parray = psa;

	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::Read (LPCTSTR pszKey, CObject& obj)
{
	BOOL	bSucess = FALSE;
	BYTE*	pData = NULL;
	UINT	uDataSize;

	if (!Read (pszKey, &pData, &uDataSize))
	{
		ASSERT (pData == NULL);
		return FALSE;
	}

	ASSERT (pData != NULL);

	try
	{
		CMemFile file (pData, uDataSize);
		CArchive ar (&file, CArchive::load);

		obj.Serialize (ar);
		bSucess = TRUE;
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Read ()!\n"));
	}
	catch (CArchiveException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("CArchiveException exception in CXMLSettings::Read ()!\n"));
	}

	delete pData;
	return bSucess;
}
//***************************************************************************************
BOOL CXMLSettings::Read (LPCTSTR pszKey, CObject*& pObj)
{
	BOOL	bSucess = FALSE;
	BYTE*	pData = NULL;
	UINT	uDataSize;

	if (!Read (pszKey, &pData, &uDataSize))
	{
		ASSERT (pData == NULL);
		return FALSE;
	}

	ASSERT (pData != NULL);

	try
	{
		CMemFile file (pData, uDataSize);
		CArchive ar (&file, CArchive::load);

		ar >> pObj;

		bSucess = TRUE;
	}
	catch (CMemoryException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("Memory exception in CXMLSettings::Read ()!\n"));
	}
	catch (CArchiveException* pEx)
	{
		pEx->Delete ();
		TRACE(_T("CArchiveException exception in CXMLSettings::Read ()!\n"));
	}

	delete pData;
	return bSucess;
}
//***************************************************************************************
BOOL CXMLSettings::DeleteValue (LPCTSTR pszValue)
{
	if (m_pCurrNode == NULL)
	{
		return FALSE;
	}

	ASSERT_VALID (m_pCurrNode);

	for (POSITION pos = m_pCurrNode->m_lstChildren.GetHeadPosition (); pos != NULL;)
	{
		POSITION posSave = pos;

		CXMLNode* pNode = m_pCurrNode->m_lstChildren.GetNext (pos);
		ASSERT_VALID (pNode);

		if (pNode->m_strName == pszValue)
		{
			m_pCurrNode->m_lstChildren.RemoveAt (posSave);
			delete pNode;
			return TRUE;
		}
	}

	return FALSE;
}
//***************************************************************************************
BOOL CXMLSettings::DeleteKey (LPCTSTR pszPath)
{
	if (m_bReadOnly)
	{
		return FALSE;
	}
	
	CXMLNode* pNode = m_pCurrNode;

	ASSERT (pszPath != NULL);
	CString strPath = pszPath;

	int iPathLen = strPath.GetLength ();
	if (iPathLen > 0 && strPath [iPathLen - 1] != _T('\\'))
	{
		strPath += _T('\\');
	}
	
	for (int iFrom = 0; iFrom < strPath.GetLength ();)
	{
		int iEnd = strPath.Find (_T('\\'), iFrom);
		if (iEnd < 0)
		{
			ASSERT (FALSE);
			return FALSE;
		}

		CString strSubKey = strPath.Mid (iFrom, iEnd - iFrom);
		strSubKey.Remove (_T(' '));
		
		CXMLNode* pChild = pNode->FindChild (strSubKey);
		if (pChild == NULL)
		{
			return FALSE;
		}

		pNode = pChild;
		iFrom = iEnd + 1;
	}

	ASSERT_VALID (pNode->m_pParent);
	for (POSITION pos = pNode->m_pParent->m_lstChildren.GetHeadPosition (); pos != NULL;)
	{
		POSITION posSave = pos;

		CXMLNode* pCurrNode = pNode->m_pParent->m_lstChildren.GetNext (pos);
		ASSERT_VALID (pCurrNode);

		if (pNode == pCurrNode)
		{
			pNode->m_pParent->m_lstChildren.RemoveAt (posSave);
			delete pNode;
			m_posChild = m_pCurrNode->m_lstChildren.GetHeadPosition();
			return TRUE;
		}
	}

	ASSERT(FALSE);
	return FALSE;
}
//***************************************************************************************
BOOL CXMLSettings::DeleteAllKeys ()
{
	if (m_bReadOnly)
	{
		return FALSE;
	}
	
	CXMLNode* pNode = &m_Local;

	while (!pNode->m_lstChildren.IsEmpty ())
	{
		delete pNode->m_lstChildren.RemoveHead ();
	}

	return TRUE;
}

//***************************************************************************************
BOOL CXMLSettings::DeleteCurrNodeChildKeys(BOOL bAll /* = TRUE */)
{
	if (m_bReadOnly)
	{
		return FALSE;
	}
	
	CXMLNode* pNode = m_pCurrNode;
	if (bAll)
	{
		while (!pNode->m_lstChildren.IsEmpty ())
		{
			delete pNode->m_lstChildren.RemoveHead ();
		}
	}
	else
	{
		CList<CXMLNode*, CXMLNode*> lstNew;
		CList<CXMLNode*, CXMLNode*> lstDelete;
		POSITION pos = pNode->m_lstChildren.GetHeadPosition();
		for ( ; pos != NULL; )
		{
			CXMLNode* pChild = pNode->m_lstChildren.GetNext(pos);
			if (pChild->m_lstChildren.GetCount())
				lstDelete.AddTail(pChild);
			else
				lstNew.AddTail(pChild);
		}

		pNode->m_lstChildren.RemoveAll();
		pNode->m_lstChildren.AddTail(&lstNew);

		while (lstDelete.GetCount())
		{
			delete lstDelete.RemoveHead();
		}
	}

	return TRUE;
}

//***************************************************************************************
BOOL CXMLSettings::ReadSubKeys(CStringList& SubKeys)
{
	if (m_pCurrNode == NULL)
		return FALSE;
	
	SubKeys.RemoveAll();
	POSITION pos = m_pCurrNode->m_lstChildren.GetHeadPosition ();
	for ( ; pos != NULL; )
	{
		CXMLNode* pNode = m_pCurrNode->m_lstChildren.GetNext (pos);
		ASSERT_VALID (pNode);
		SubKeys.AddTail(pNode->m_strName);
	}
	
	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::ReadKeyValues(CStringArray& Values)
{
	if (m_pCurrNode == NULL)
		return FALSE;
	
	Values.RemoveAll();
	POSITION pos = m_pCurrNode->m_lstChildren.GetHeadPosition ();
	for ( ; pos != NULL; )
	{
		CXMLNode* pNode = m_pCurrNode->m_lstChildren.GetNext (pos);
		ASSERT_VALID (pNode);
		Values.Add(pNode->m_strValue);
	}
		
	return TRUE;
}
//***************************************************************************************
LPCTSTR CXMLSettings::ReadTag (LPCTSTR pszKey) const
{
	ASSERT (pszKey != NULL);

	if (m_pCurrNode == NULL)
	{
		return NULL;
	}

	ASSERT_VALID (m_pCurrNode);

	CString strKey = pszKey;
	strKey.Remove (_T(' '));
	if (strKey.IsEmpty())
	{
		return m_pCurrNode->m_strValue;
	}

	CXMLNode* pNode = m_pCurrNode->FindChild (strKey, (POSITION&)m_posChild);
	if (pNode == NULL)
	{
		return NULL;
	}

	return pNode->m_strValue;
}
//***************************************************************************************
BOOL CXMLSettings::WriteXMLToFile (LPCTSTR lpszFileName)
{
	CXMLNode& node = m_Local;

	// Create XML buffer:
	CString strBuffer;
	if (node.WriteToBuffer (strBuffer, 0, 0) <= 0)
	{
		return FALSE;
	}

	try
	{
		CStdioFile file;
		if (file.Open (lpszFileName,	CFile::modeWrite|CFile::modeCreate|CFile::typeText))
			file.WriteString (strBuffer);
		else
			return FALSE;
	}
	catch (CFileException* pEx)
	{
		pEx->ReportError ();
		pEx->Delete ();

		return FALSE;
	}

	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::WriteCurrNodeToFile(LPCTSTR lpszFileName)
{
	if (m_pCurrNode == NULL)
		return FALSE;

	// Create XML buffer:
	CString strBuffer;
	if (m_pCurrNode->WriteToBuffer (strBuffer, 0, 0) <= 0)
	{
		return FALSE;
	}

	try
	{
		CStdioFile file;
		if (file.Open (lpszFileName,	CFile::modeWrite|CFile::modeCreate|CFile::typeText))
			file.WriteString (strBuffer);
		else
			return FALSE;
	}
	catch (CFileException* pEx)
	{
		pEx->ReportError ();
		pEx->Delete ();

		return FALSE;
	}

	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::ReadXMLFromFile (LPCTSTR lpszFileName)
{
	CString strBuffer;

	try
	{
		CStdioFile file;
		if (!file.Open (lpszFileName, CFile::modeRead))
		{
			TRACE(_T("File not found: %s\n"), lpszFileName);
			return FALSE;
		}

		CString str;

		while (file.ReadString (str))
		{
			int iTagStart = str.Find('<');
			str.TrimLeft(_T("\t"));
			str.TrimLeft();
// 			if (iTagStart > 0)
// 				str.Delete(0, iTagStart);
			strBuffer += str;
		}
	}
	catch (CFileException* pEx)
	{
		pEx->ReportError ();
		pEx->Delete ();

		return FALSE;
	}

	if(strBuffer.IsEmpty())
		return FALSE;

	CXMLNode& node = m_Local;
	CStringList stack;
	if (node.ReadFromBuffer2 (strBuffer, &stack))
	{
		m_pCurrNode = &m_Local;
		m_posChild = NULL;
		return TRUE;
	}

	return FALSE;
}
//***************************************************************************************
BOOL CXMLSettings::RereadXMLFromFile (LPCTSTR lpszFileName)
{
	CString strBuffer;

	try
	{
		CStdioFile file;
		if (!file.Open (lpszFileName, CFile::modeRead))
		{
			TRACE(_T("File not found: %s\n"), lpszFileName);
			return FALSE;
		}

		CString str;

		while (file.ReadString (str))
		{
			int iTagStart = str.Find('<');
			if (iTagStart > -1)
				strBuffer += str.Right(str.GetLength() - iTagStart);
		}
	}
	catch (CFileException* pEx)
	{
		pEx->ReportError ();
		pEx->Delete ();

		return FALSE;
	}

	if(strBuffer.IsEmpty())
		return FALSE;

	CXMLNode& node = m_Local;
	return node.RereadFromBuffer (strBuffer);
}
//***************************************************************************************
BOOL CXMLSettings::ReadCurrNodeFromFile (LPCTSTR lpszFileName, BOOL bRebuild /* = TRUE */)
{
	if (m_pCurrNode == NULL)
		return FALSE;

	CString strBuffer;

	try
	{
		CStdioFile file;
		if (!file.Open (lpszFileName, CFile::modeRead))
		{
			TRACE(_T("File not found: %s\n"), lpszFileName);
			return FALSE;
		}

		CString str;

		while (file.ReadString (str))
		{
			int iTagStart = str.Find('<');
			if(iTagStart > -1)
				strBuffer += str.Right(str.GetLength() - iTagStart);
		}
	}
	catch (CFileException* pEx)
	{
		pEx->ReportError ();
		pEx->Delete ();

		return FALSE;
	}

	if (strBuffer.IsEmpty())
		return FALSE;

	if (bRebuild)
	{
		CStringList stack;
		return m_pCurrNode->ReadFromBuffer2 (strBuffer, &stack);
	}

	return m_pCurrNode->RereadFromBuffer (strBuffer);
}
//***************************************************************************************
BOOL CXMLSettings::ReadXMLFromFile (LPCTSTR lpszFileName, LPCTSTR lpszKey)
{
	CString strBuffer;

	try
	{
		CStdioFile file;
		if (!file.Open (lpszFileName, CFile::modeRead))
		{
			TRACE(_T("File not found: %s\n"), lpszFileName);
			return FALSE;
		}

		CString str;

		while (file.ReadString (str))
		{
			int iTagStart = str.Find('<');
			if(iTagStart > -1)
				strBuffer += str.Right(str.GetLength() - iTagStart);
		}
	}
	catch (CFileException* pEx)
	{
		pEx->ReportError ();
		pEx->Delete ();

		return FALSE;
	}

	if (strBuffer.IsEmpty())
		return FALSE;

	CXMLNode& node = m_Local;
	return node.ReadFromBuffer (strBuffer, lpszKey);
}
//***************************************************************************************
BOOL CXMLSettings::WriteXMLToText(LPCTSTR lpszTextName)
{
	CStdioFile file;
	if (!file.Open (lpszTextName, CFile::modeWrite|CFile::modeCreate|CFile::typeText))
		return FALSE;

	CXMLNode& node = m_Local;
	CString strBuffer = _T("");
	node.WriteToText(file, strBuffer);
	
	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::WriteXMLToStream(IStreamPtr& pStream)
{
	CXMLNode& node = m_Local;
	
	// Create XML buffer:
	CString strBuffer;
	if (node.WriteToBuffer (strBuffer, 0) <= 0)
	{
		return FALSE;
	}
	
	ULONG cbLen = strBuffer.GetLength();
	pStream->Write(&cbLen, sizeof(ULONG), NULL);
	pStream->Write(strBuffer.GetBuffer(cbLen), cbLen, NULL);
	strBuffer.ReleaseBuffer(cbLen);
	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::ReadXMLFromStream(IStreamPtr& pStream, LPCTSTR lpszKey /* = NULL */)
{
	ULONG cbLen = 0;
	HRESULT hr = pStream->Read((void*) &cbLen, sizeof(cbLen), NULL);
	CString strBuffer;
	if ((hr == S_OK) && (cbLen != 0))
	{
		//subtract size for terminating NULL which we wrote out
		//since SysAllocStringByteLen overallocates for the NULL
		LPTSTR lpTSTR = strBuffer.GetBufferSetLength(cbLen);
		if (lpTSTR == NULL)
			hr = E_OUTOFMEMORY;
		else
			hr = pStream->Read((void*) lpTSTR, cbLen, NULL);
	}
	
	if (hr == S_FALSE)
		hr = E_FAIL;
	
	if(FAILED(hr))
		return FALSE;
	
	if(strBuffer.IsEmpty())
		return FALSE;
	
	CXMLNode& node = m_Local;
	if (lpszKey != NULL)
		return node.ReadFromBuffer(strBuffer, lpszKey);

	CStringList stack;
	return node.ReadFromBuffer2(strBuffer, &stack);
}
//***************************************************************************************
BOOL CXMLSettings::RereadXMLFromStream(IStreamPtr& pStream)
{
	ULONG cbLen = 0;
	HRESULT hr = pStream->Read((void*) &cbLen, sizeof(cbLen), NULL);
	CString strBuffer = _T("");
	if ((hr == S_OK) && (cbLen != 0))
	{
		//subtract size for terminating NULL which we wrote out
		//since SysAllocStringByteLen overallocates for the NULL
		LPTSTR lpTSTR = strBuffer.GetBufferSetLength(cbLen);
		if (lpTSTR == NULL)
			hr = E_OUTOFMEMORY;
		else
			hr = pStream->Read((void*) lpTSTR, cbLen, NULL);
	}
	
	if (hr == S_FALSE)
		hr = E_FAIL;
	
	if (FAILED(hr))
		return FALSE;
	
	if (strBuffer.IsEmpty())
		return FALSE;
	
	return m_Local.RereadFromBuffer(strBuffer);
}
//***************************************************************************************
BOOL CXMLSettings::ReadXMLFromStream(IStreamPtr& pStream, LPCTSTR pszKey0, LPCTSTR pszKey1)
{
	ULONG cbLen = 0;
	HRESULT hr = pStream->Read((void*) &cbLen, sizeof(cbLen), NULL);
	CString strBuffer = _T("");
	if ((hr == S_OK) && (cbLen != 0))
	{
		//subtract size for terminating NULL which we wrote out
		//since SysAllocStringByteLen overallocates for the NULL
		LPTSTR lpTSTR = strBuffer.GetBufferSetLength(cbLen);
		if (lpTSTR == NULL)
			hr = E_OUTOFMEMORY;
		else
			hr = pStream->Read((void*) lpTSTR, cbLen, NULL);
	}
	
	if (hr == S_FALSE)
		hr = E_FAIL;
	
	if (FAILED(hr))
		return FALSE;
	
	if (strBuffer.IsEmpty())
		return FALSE;
	
	CXMLNode& node = m_Local;
	CString strBuffer1 = strBuffer;

	CXMLNode* pChild0 = new CXMLNode;
	if (pChild0->ReadFromBuffer(strBuffer, pszKey0))
		node.AddChild(pChild0);
	else
		delete pChild0;
	
	CXMLNode* pChild1 = new CXMLNode;
	if (pChild1->ReadFromBuffer(strBuffer1, pszKey1))
	{
		node.AddChild(pChild1);
	}
	else
		delete pChild1;

	return node.m_lstChildren.GetCount();
}
//***************************************************************************************
BOOL CXMLSettings::WriteXMLToBuffer(CString& strBuffer)
{
	CXMLNode& node = m_Local;
	
	// Create XML buffer:
	if (node.WriteToBuffer (strBuffer, 0) <= 0)
	{
		return FALSE;
	}

	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::WriteXMLToFile (CFile* pFile)
{
	CXMLNode& node = m_Local;
	
	// Create XML buffer:
	CString strBuffer;
	
	if (node.WriteToBuffer (strBuffer, 0) <= 0)
	{
		return FALSE;
	}

	DWORD cbLen = strBuffer.GetLength();
	pFile->Write(&cbLen, sizeof(DWORD));
	pFile->Write(strBuffer.GetBuffer(cbLen), cbLen);
	strBuffer.ReleaseBuffer();
	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::WriteXMLToFiles (CFile* pFile0, ...)
{
	CXMLNode& node = m_Local;
	
	// Create XML buffer:
	CString strBuffer;

	if (node.WriteToBuffer (strBuffer, 0) <= 0)
	{
		return FALSE;
	}
	
	DWORD cbLen = strBuffer.GetLength();


	CFile* p = pFile0;

	va_list marker;
	
	va_start( marker, pFile0 );     /* Initialize variable arguments. */

	void* pBuffer = strBuffer.GetBuffer(cbLen);
	while( p != NULL )
	{
		p->Write(&cbLen, sizeof(DWORD));
		p->Write(pBuffer, cbLen);
		
		p = va_arg( marker, CFile*);
	}
	strBuffer.ReleaseBuffer();

	va_end( marker );              /* Reset variable arguments.      */

	return TRUE;
}
//***************************************************************************************
BOOL CXMLSettings::ReadXMLFromBuffer(CString& strBuffer)
{
	if (strBuffer.IsEmpty())
		return FALSE;
	
	CXMLNode& node = m_Local;
	CStringList stack;
	return node.ReadFromBuffer2(strBuffer, &stack);
}
//***************************************************************************************
BOOL CXMLSettings::ReadXMLFromFile (CFile* pFile)
{
	DWORD cbLen = 0;
	CString strBuffer = _T("");
	try
	{
		pFile->Read(&cbLen, sizeof(DWORD));
		int nLen = cbLen;
		if (nLen > 0)
		{
#ifdef _UNICODE
			char* pBuffer = new char[cbLen];
			pFile->Read((void*) pBuffer, cbLen);
			USES_CONVERSION;
			strBuffer = A2W(pBuffer);
			delete pBuffer;
#else
			LPTSTR lpTSTR = strBuffer.GetBufferSetLength(cbLen);
			if (lpTSTR == NULL)
				return FALSE;
			pFile->Read((void*) lpTSTR, cbLen);
#endif
		}	
	}
	catch (CFileException* pEx)
	{
		pEx->ReportError ();
		pEx->Delete ();
		return FALSE;
	}
	catch (CMemoryException* pEx) 
	{
		// pEx->ReportError();
		pEx->Delete();
		return FALSE;
	}

	if (strBuffer.IsEmpty())
		return FALSE;
	
	CXMLNode& node = m_Local;
	CStringList stack;
	return node.ReadFromBuffer2(strBuffer, &stack);
}
//***************************************************************************************
BOOL CXMLSettings::ReadXMLFromFile (CFile* pFile, LPCTSTR pszKey0, LPCTSTR pszKey1)
{
	DWORD cbLen = 0;
	pFile->Read(&cbLen, sizeof(DWORD));
	CString strBuffer = _T("");
	if(cbLen > 0)
	{
		LPTSTR lpTSTR = strBuffer.GetBufferSetLength(cbLen);
		if (lpTSTR == NULL)
			return FALSE;
		pFile->Read((void*) lpTSTR, cbLen);
	}

	if (strBuffer.IsEmpty())
		return FALSE;
	
	CXMLNode& node = m_Local;
	CString strBuffer1 = strBuffer;
	
	CXMLNode* pChild0 = new CXMLNode;
	if (pChild0->ReadFromBuffer(strBuffer, pszKey0))
		node.AddChild(pChild0);
	else
		delete pChild0;
	
	CXMLNode* pChild1 = new CXMLNode;
	if (pChild1->ReadFromBuffer(strBuffer1, pszKey1))
	{
		node.AddChild(pChild1);
	}
	else
		delete pChild1;
	
	return node.m_lstChildren.GetCount();
}
//***************************************************************************************
BOOL CXMLSettings::GetChildFromFile (CFile* pFile)
{
	DWORD cbLen = 0;
	pFile->Read(&cbLen, sizeof(DWORD));
	CString strBuffer = _T("");
	if(cbLen > 0)
	{
		LPTSTR lpTSTR = strBuffer.GetBufferSetLength(cbLen);
		if (lpTSTR == NULL)
			return FALSE;
		pFile->Read((void*) lpTSTR, cbLen);
	}

	if(strBuffer.IsEmpty())
		return FALSE;
	
	CXMLNode* pXMLNode = new CXMLNode;
	m_Local.AddChild(pXMLNode);

	return pXMLNode->ReadFromBuffer(strBuffer);
}
//***************************************************************************************
int CXMLNode::WriteToBuffer (CString& strBuffer, int iOffset)
{
	if (m_dwFlag & XNF_NOTSAVE)
		return 0;

	if (m_lstChildren.IsEmpty () && m_pParent != NULL)
	{
		CString strTagValue;
		strTagValue.Format (_T("<%s>\"%s\"</%s>"), m_strName, m_strValue, m_strName);

		strBuffer.Insert (iOffset, strTagValue);
		return strTagValue.GetLength ();
	}

	int iOffsetOrig = iOffset;

	CString strTagStart;
	strTagStart.Format (_T("<%s>"), m_strName);

	strBuffer.Insert (iOffset, strTagStart);
	iOffset += strTagStart.GetLength ();

	for (POSITION pos = m_lstChildren.GetHeadPosition (); pos != NULL;)
	{
		CXMLNode* pNode = m_lstChildren.GetNext (pos);
		ASSERT_VALID (pNode);

		iOffset += pNode->WriteToBuffer (strBuffer, iOffset);
	}

	CString strTagEnd;
	strTagEnd.Format (_T("</%s>"), m_strName);

	strBuffer.Insert (iOffset, strTagEnd);
	iOffset += strTagEnd.GetLength ();

	return iOffset - iOffsetOrig;
}
//***************************************************************************************
int CXMLNode::WriteToBuffer (CString& strBuffer, int iOffset, int iLevel)
{
	if (m_dwFlag & XNF_NOTSAVE)
		return 0;

	CString strTagTab = _T("");
	for (int i=0; i<iLevel; i++)
		strTagTab += m_strTab;

	if (m_lstChildren.IsEmpty () && m_pParent != NULL)
	{
		CString strTagValue;		
		strTagValue.Format (_T("<%s>\"%s\"</%s>\n"), m_strName, m_strValue, m_strName);
		strTagValue = strTagTab + strTagValue;
		strBuffer.Insert (iOffset, strTagValue);
		return strTagValue.GetLength ();
	}

	int iOffsetOrig = iOffset;

	CString strTagStart;
	strTagStart.Format (_T("<%s>\n"), m_strName);
	strTagStart = strTagTab + strTagStart;

	strBuffer.Insert (iOffset, strTagStart);
	iOffset += strTagStart.GetLength ();

	for (POSITION pos = m_lstChildren.GetHeadPosition (); pos != NULL;)
	{
		CXMLNode* pNode = m_lstChildren.GetNext (pos);
		ASSERT_VALID (pNode);

		iOffset += pNode->WriteToBuffer (strBuffer, iOffset, iLevel+1);
	}

	CString strTagEnd;
	strTagEnd.Format (_T("</%s>\n"), m_strName);
	strTagEnd = strTagTab + strTagEnd;

	strBuffer.Insert (iOffset, strTagEnd);
	iOffset += strTagEnd.GetLength ();

	return iOffset - iOffsetOrig;
}
//***************************************************************************************
int CXMLNode::WriteToText (CStdioFile& file, CString& strBuffer)
{
	if (m_lstChildren.GetCount())
	{
		CString strText = strBuffer;
		if (m_pParent == NULL)
		{
			CString strTitle = m_strName + _T("\n");
			file.WriteString(strTitle);
		}
		else
		{
			strText += m_strName;
			strText += _T("\t");
		}
		
		for (POSITION pos = m_lstChildren.GetHeadPosition(); pos != NULL; )
		{
			CXMLNode* pNode = m_lstChildren.GetNext(pos);
			pNode->WriteToText(file, strText);
		}
	}
	else
	{
		if (m_pParent == NULL)
		{
			file.WriteString(m_strName);
			return 0;
		}

		POSITION pos = m_pParent->m_lstChildren.Find(this);
		m_pParent->m_lstChildren.GetNext(pos);
		if (pos != NULL)
		{
			strBuffer += m_strValue;
			strBuffer += _T("\t");
		}
		else
		{
			CString strText = strBuffer;
			strText += m_strValue;
			strText += _T("\n");
			file.WriteString(strText);
		}
	}

	return 0;
}
//***************************************************************************************
BOOL ExcludeTag2 (CString& strBuffer, CStringList* pList, CString& strTag)
{
	if (pList->IsEmpty())
		return FALSE;
	
	const int iBufLen = strBuffer.GetLength ();
// 	TRACE("\n[%d]", g_count++);
//	TRACE(strBuffer);

	CString strTagEnd = _T("/");
	strTagEnd += pList->GetHead();
	strTagEnd += _T('>');
	int iTagEndLen = strTagEnd.GetLength();

	for (int i = 2; i < iBufLen; i ++)
	{
		if (strBuffer [i] != _T('<'))
		{
			continue;
		}

		if (_tcsncmp (strBuffer.Mid (i + 1), strTagEnd, iTagEndLen) == 0)
		{
			if (strBuffer[i - 1] != _T('\"'))
				return FALSE;

			strTag = strBuffer.Mid (1, i - 2);
// 			strTag.TrimLeft ();
// 			strTag.TrimRight ();

			strBuffer.Delete(0, i + iTagEndLen + 1);
			pList->RemoveHead();
			return TRUE;
		}
	}

	return FALSE;
}
//***************************************************************************************
BOOL ExcludeTag3 (CString& strBuffer, CStringList* pList, CString& strTag)
{
	if (pList->IsEmpty())
		return FALSE;
	
	const int iBufLen = strBuffer.GetLength ();
// 	TRACE("\n[%d]", g_count++);
//	TRACE(strBuffer);

	CString strTagEnd = _T("/");
	strTagEnd += pList->GetHead();
	strTagEnd += _T('>');
	int iTagEndLen = strTagEnd.GetLength();

	for (int i = 0; i < iBufLen; i ++)
	{
		if (strBuffer [i] != '<')
		{
			continue;
		}

		if (_tcsncmp (strBuffer.Mid (i + 1), strTagEnd, iTagEndLen) == 0)
		{
			if (i > 1)
			{
				if (strBuffer[i - 1] != '\"')
					return FALSE;

				strTag = strBuffer.Mid (1, i - 2);
// 				strTag.TrimLeft ();
//				strTag.TrimRight ();
			}
			
			strBuffer.Delete (0, i + iTagEndLen + 1);
			pList->RemoveHead();
			return TRUE;
		}
	}

	return FALSE;
}
//***************************************************************************************
BOOL ExcludeTag (CString& strBuffer, LPCTSTR lpszTag, CString& strTag)
{
	const int iBufLen = strBuffer.GetLength ();
	CString strTagStart = _T("<");
	strTagStart += lpszTag;
	strTagStart += _T(">");

	const int iTagStartLen = strTagStart.GetLength ();

	int iStart = -1;

	int iIndexStart = strBuffer.Find (strTagStart);
	if (iIndexStart < 0)
	{
		return FALSE;
	}

	iStart = iIndexStart + iTagStartLen;

	CString strTagEnd = _T("</");
	strTagEnd += lpszTag;
	strTagEnd += _T('>');

	const int iTagEndLen = strTagEnd.GetLength ();

	int iIndexEnd =  -1;
	int nBalanse = 1;
	for (int i = iStart; i < iBufLen - iTagEndLen + 1; i ++)
	{
		if (strBuffer [i] != '<')
		{
			continue;
		}

		if (i < iBufLen - iTagStartLen &&
			_tcsncmp (strBuffer.Mid (i), strTagStart, iTagStartLen) == 0)
		{
			i += iTagStartLen - 1;
			nBalanse ++;
			continue;
		}

		if (_tcsncmp (strBuffer.Mid (i), strTagEnd, iTagEndLen) == 0)
		{
			nBalanse --;
			if (nBalanse == 0)
			{
				iIndexEnd = i;
				break;
			}

			i += iTagEndLen - 1;
		}
	}

	if (iIndexEnd == -1 || iStart > iIndexEnd)
	{
		return FALSE;
	}

	strTag = strBuffer.Mid (iStart, iIndexEnd - iStart);
	strTag.TrimLeft ();
	strTag.TrimRight ();

	strBuffer.Delete (iIndexStart, iIndexEnd + iTagEndLen - iIndexStart);
	return TRUE;
}
//***************************************************************************************
BOOL CXMLNode::ReadFromBuffer (CString& strBuffer)
{
	m_strValue.Empty ();

	while (m_lstChildren.GetCount ())
	{
		delete m_lstChildren.RemoveHead ();
	}
		
	if (strBuffer [0] != '<')
	{
		// ASSERT(FALSE);
		return FALSE;	
	}

	int iTagEnd = strBuffer.Find ('>');
	if (iTagEnd < 0)
	{
		// ASSERT(FALSE);
		return FALSE;
	}

	m_strName = strBuffer.Mid (1, iTagEnd - 1);

	CString strTag;
	if (!ExcludeTag (strBuffer, m_strName, strTag))
	{
		// ASSERT (FALSE);
		return FALSE;
	}

	if (strTag.IsEmpty ())
	{
		return TRUE;
	}

	if (strTag [0] == '\"')
	{
		ASSERT (strTag [strTag.GetLength () - 1] == '\"');
		m_strValue = strTag.Mid (1, strTag.GetLength () - 2);

		return TRUE;
	}

	if (strTag [0] != '<')
	{
		ASSERT(FALSE);
		return FALSE;
	}

	while (strTag.GetLength ())
	{
		CXMLNode* pChild = new CXMLNode;
		if (!pChild->ReadFromBuffer (strTag))
		{
			delete pChild;
			return FALSE;
		}

		AddChild (pChild);
	}

	return TRUE;
}
//***************************************************************************************
BOOL CXMLNode::ReadFromBuffer2 (CString& strBuffer, CStringList* pList)
{
	m_strValue.Empty ();

	while (m_lstChildren.GetCount ())
	{
		delete m_lstChildren.RemoveHead ();
	}
	
	if (strBuffer [0] != '<')
	{
		return FALSE;	
	}

	int iTagEnd = strBuffer.Find ('>');
	if (iTagEnd < 0)
	{
		return FALSE;
	}

	m_strName = strBuffer.Mid (1, iTagEnd - 1);
	pList->AddHead (m_strName);

	strBuffer.Delete(0, ++iTagEnd);
	strBuffer.TrimLeft();

	switch (strBuffer [0])
	{
	case '\"':
		{
			if (ExcludeTag2 (strBuffer, pList, m_strValue))
				return TRUE;
		}
	case '<':
		{
			while (strBuffer.GetLength())
			{
				if (strBuffer [1] == '/')
				{
					if (ExcludeTag3 (strBuffer, pList, m_strValue))
						return TRUE;
				}

				// TRACE("\n[x%d]", g_count++);
				// TRACE(strBuffer);
				CXMLNode* pChild = new CXMLNode;
				if (!pChild->ReadFromBuffer2 (strBuffer, pList))
				{
					delete pChild;
					return FALSE;
				}

				AddChild (pChild);
			}

			return TRUE;
		}
		break;
	default:
		break;
	}

	return FALSE;
}
//***************************************************************************************
BOOL CXMLNode::RereadFromBuffer (CString& strBuffer)
{
	if (strBuffer [0] != '<')
	{
		return FALSE;	
	}

	int iTagEnd = strBuffer.Find ('>');
	if (iTagEnd < 0)
	{
		return FALSE;
	}

	// m_strName = strBuffer.Mid (1, iTagEnd - 1);

	CString strTag;
	if (!ExcludeTag (strBuffer, m_strName, strTag))
	{
		return FALSE;
	}

	if (strTag.IsEmpty ())
	{
		return TRUE;
	}

	if (strTag [0] == '\"')
	{
		ASSERT (strTag [strTag.GetLength () - 1] == '\"');
		m_strValue = strTag.Mid (1, strTag.GetLength () - 2);

		return TRUE;
	}

	if (strTag [0] != '<')
	{
		ASSERT(FALSE);
		return FALSE;
	}

	POSITION pos = m_lstChildren.GetHeadPosition();
	for ( ; (pos != NULL) && strTag.GetLength(); )
	{
		CXMLNode* pChild = m_lstChildren.GetNext(pos);
		pChild->RereadFromBuffer(strTag);
	}

	return TRUE;
}
//***************************************************************************************
BOOL CXMLNode::ReadFromBuffer (CString& strBuffer, LPCTSTR lpszPath)
{
	m_strValue.Empty ();

	while (!m_lstChildren.IsEmpty ())
	{
		delete m_lstChildren.RemoveHead ();
	}
	
	CString strPath = lpszPath;
	int iPathLen = strPath.GetLength ();
	if (iPathLen < 1)
		return TRUE;

	if (strPath [iPathLen - 1] != _T('\\'))
	{
		strPath += _T('\\');
	}

	int iEnd = strPath.Find (_T('\\'));
	if (iEnd < 0)
	{
		// ASSERT (FALSE);
		return FALSE;
	}

	CString strSubKey = strPath.Left(iEnd);
	strSubKey.Remove (_T(' '));
	CString strTag;
	if (!ExcludeTag (strBuffer, strSubKey, strTag))
		return FALSE;

	if (strTag.IsEmpty ())
		return TRUE;
	
	if (strTag [0] == '\"')
	{
		ASSERT (strTag [strTag.GetLength () - 1] == '\"');
		m_strValue = strTag.Mid (1, strTag.GetLength () - 2);
		m_strName = strSubKey;
		return TRUE;
	}
	
	if (strTag [0] != '<')
	{
		ASSERT(FALSE);
		return FALSE;
	}

	CString strSubPath = strPath.Right(iPathLen - iEnd);
	CXMLNode* pChild = new CXMLNode;
	if (!pChild->ReadFromBuffer (strTag, strSubPath))
	{
		delete pChild;
		return FALSE;
	}
	AddChild (pChild);
	return TRUE;
}
//***************************************************************************************
CXMLNode* CXMLSettings::GetTree ()
{
	return &m_Local;
}

BOOL CXMLSettings::OpenNext(LPCTSTR pszPath)
{
	ASSERT (pszPath != NULL);
	m_sPath = pszPath;
	
	CXMLNode* pParent = m_pCurrNode->m_pParent ;
	for (POSITION pos = pParent->m_lstChildren.GetHeadPosition (); pos != NULL;)
	{
		CXMLNode* pNode = pParent->m_lstChildren.GetNext (pos);
		if (pNode == m_pCurrNode && pos != NULL)
		{
			CXMLNode* pNewNode = pParent->m_lstChildren.GetNext (pos);
			if(pNewNode->m_strName == pszPath)
			{
				m_pCurrNode = pNewNode;
				return TRUE;
			}
		}
	}
	return FALSE;	

}

BOOL CXMLSettings::Read(CString &sVal)
{
	sVal = ReadTag(_T(""));
	return TRUE;

}
